﻿/* *************************************************
 *  Copyright © FlexDev 2010. All rights reserved.
 *  Author: Rodrigo da Silva Brito
 *  Date: 07/16/2010
 *  Contact: mailto:rodrigosilvabrito@hotmail.com
 *  Site: http://www.flexdev.com.br
 * *************************************************
*/
using System;
using System.IdentityModel.Selectors;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Security;
using System.Xml;

namespace Flexdev.Wcf.Extensions
{
    public class FlexdevMessageInspector : IClientMessageInspector, IDispatchMessageInspector
    {
        private string userName = string.Empty, password = string.Empty;

        public FlexdevMessageInspector()
        {
        }

        public FlexdevMessageInspector(string userName, string password)
        {
            this.userName = userName;
            this.password = password;
        }

        #region IClientMessageInspector Members

        public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
        }

        public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
        {
            // Prepare the request message copy to be modified
            MessageBuffer buffer = request.CreateBufferedCopy(int.MaxValue);
            request = buffer.CreateMessage();

            request.Headers.Add(new FlexdevHeader(userName, password));

            return null;
        }

        #endregion

        #region IDispatchMessageInspector Members

        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            // Look for FlexdevHeader in the request
            int headerPosition = request.Headers.FindHeader(FlexdevHeaderNames.HeaderName, FlexdevHeaderNames.HeaderNameSpace);

            if (headerPosition >= 0)
            {
                // Get an XmlDictionaryReader to read the header content
                XmlDictionaryReader reader = request.Headers.GetReaderAtHeader(headerPosition);

                // Read it through its static method ReadHeader
                FlexdevHeader header = FlexdevHeader.ReadHeader(reader);

                //Validate user and password
                ServiceCredentials serviceCredentials = instanceContext.Host.Credentials;
                if (serviceCredentials == null)
                    throw new NotImplementedException("The credentials of the service were not implemented.");

                UserNamePasswordServiceCredential userNamePasswordServiceCredential = serviceCredentials.UserNameAuthentication;
                if (userNamePasswordServiceCredential != null)
                {
                    UserNamePasswordValidator customUserNamePasswordValidator = userNamePasswordServiceCredential.CustomUserNamePasswordValidator;
                    if (customUserNamePasswordValidator == null)
                        throw new NotImplementedException("The class that implements UserNamePasswordValidator was not defined in the configuration of your application. Use the mark customUserNamePasswordValidatorType = \"YourValidatorClass, YourAssembly\"");

                    customUserNamePasswordValidator.Validate(header.UserName, header.Password);
                }

                // Add the content of the header to the IncomingMessageProperties dictionary
                OperationContext.Current.IncomingMessageProperties.Add("userName", header.UserName);
                OperationContext.Current.IncomingMessageProperties.Add("password", header.Password);
            }
            else
                throw new InvalidMessageContractException("Invalid client request. Expected flexdev proxy.");

            return null;
        }

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
        }

        #endregion
    }
}
